package core;
import java.util.HashMap;
import java.util.Iterator;

public class FlowGraph {
	public SimpleGraph graph;
	public HashMap<Edge, EdgeData> edgesMap = new HashMap<Edge, EdgeData>();
	public HashMap<Vertex,VertexData> verticesMap = new HashMap<Vertex, VertexData>();
	private Vertex source;
	private Vertex sink;
	
	public Vertex getSource() {
		return source;
	}

	public void setSource(Vertex source) {
		this.source = source;
	}

	public Vertex getSink() {
		return sink;
	}

	public void setSink(Vertex sink) {
		this.sink = sink;
	}

	public HashMap<Edge, EdgeData> getEdgesMap() {
		return edgesMap;
	}

	public void setEdgesMap(HashMap<Edge, EdgeData> edgesMap) {
		this.edgesMap = edgesMap;
	}

	public HashMap<Vertex, VertexData> getVerticesMap() {
		return verticesMap;
	}

	public void setVerticesMap(HashMap<Vertex, VertexData> verticesMap) {
		this.verticesMap = verticesMap;
	}
	
	public FlowGraph(SimpleGraph graph) {
		this.graph = getFlowGraph(graph);
	}
	
	@SuppressWarnings("unchecked")
	public SimpleGraph getFlowGraph(SimpleGraph graph) {
		SimpleGraph result = new SimpleGraph();
		Iterator<Edge> edgesIterator = graph.edges();
		while (edgesIterator.hasNext()) {
			Edge edge = edgesIterator.next();
			EdgeData edgeData = new EdgeData();
			edgeData.setCapacity((Double.parseDouble(edge.getData().toString())));
			getEdgesMap().put(edge, edgeData);
			result.edgeList.add(edge);
			Edge residualEdge = getEdge(edge.getSecondEndpoint(), edge.getFirstEndpoint());
			if( residualEdge == null){
				residualEdge = new Edge(edge.getSecondEndpoint(), edge.getFirstEndpoint(), 0, new Object());
				EdgeData residualEdgeData = new EdgeData();
				getEdgesMap().put(residualEdge, residualEdgeData);
			}
		}
		Iterator<Vertex> verticesIterator = graph.vertices();
		while (verticesIterator.hasNext()) {
			Vertex vertex = verticesIterator.next();
			VertexData data = new VertexData();
			getVerticesMap().put(vertex, data);
			if(vertex.getName().toString().equalsIgnoreCase("s")){setSource(vertex);}
			if(vertex.getName().toString().equalsIgnoreCase("t")){setSink(vertex);}
			result.vertexList.add(vertex);
		}
		return result;
	}

	public SimpleGraph getGraph() {
		return graph;
	}

	public void setGraph(SimpleGraph graph) {
		this.graph = graph;
	}
	
	public double augment(Path path) {
		System.out.println(path);
		double bottleneck = path.getBottleneck();
		for (int i = 0; i < path.getEdges().size(); i++) {
			Edge edge = path.getEdges().get(i);
			EdgeData edgeData = getEdgesMap().get(edge);
			edgeData.increasFlowBy(bottleneck);
			edgeData.setCapacity(edgeData.getCapacity()-bottleneck);
			edge.setData(edgeData.getCapacity());
			Edge residualEdge = getEdge(edge.getSecondEndpoint(), edge.getFirstEndpoint());
			EdgeData residualEdgeData = getEdgesMap().get(residualEdge);
			residualEdgeData.decreaseFlowBy(bottleneck);
			residualEdgeData.setCapacity(residualEdgeData.getCapacity()-residualEdgeData.getFlow());
			residualEdge.setData(residualEdgeData.getCapacity());
		}
		System.out.println(path);
		return bottleneck;
	}
	
	public void unVisitEdges(){
		Iterator<Vertex> verticesIterator = getVerticesMap().keySet().iterator();
		while(verticesIterator.hasNext()){
			(getVerticesMap().get(verticesIterator.next())).setVisited(false);
		}
	}
	
	public Edge getEdge(Vertex from, Vertex to){
		Iterator<Edge> edgesIterator = getEdgesMap().keySet().iterator();
		while(edgesIterator.hasNext()){
			Edge edge = edgesIterator.next();
			if(edge.getFirstEndpoint()==from && edge.getSecondEndpoint()==to){
				return edge;
			}
		}
		return null;
	}
	
	@Override
	public String toString(){
		Iterator<Edge> edgesIterator = edgesMap.keySet().iterator();
		Edge edge;
		String result = "";
		while(edgesIterator.hasNext()){
			edge = edgesIterator.next();
			result += edge.getFirstEndpoint().getName()+"\t"+edge.getSecondEndpoint().getName()+"\t"+edge.getData()+"\n";
		}
		result += "------------";
		return result;
	}
}
